package cn.leedsoft.luma.common.utils;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
 * Created by leeds on 2016/11/7 12:45.
 */
public class EncryptUtil {
    private static final String PWD_SALT = "leedsoft";
    private static final String AES_KEY = "leedsoft";

    //----------------------------------------------
    //依赖shiro api的、md5密码加密
    //----------------------------------------------

    /**
     * 验证密码是否正确
     *
     * @param originPwd
     * @param encryptPwd
     * @return
     */
    public static boolean chkPwd(String originPwd, String encryptPwd) {
        return encryptPwd(originPwd).equals(encryptPwd);
    }

    /**
     * 对原始密码加密，返回32位密文
     *
     * @param originPwd
     * @return
     */
    public static String encryptPwd(String originPwd) {
        String result = new SimpleHash(
                "md5",
                originPwd,
                ByteSource.Util.bytes(PWD_SALT),
                2).toHex();
        return result;
    }

    //------------------------------------------------
    //编码：
    // 1、toHex / fromHex
    // 2、toBase64 / fromBase64
    // 3、toBase64UrlEncode / fromBase64UrlEncode
    //------------------------------------------------

    /**
     * 将byte[]转为各种进制的字符串
     *
     * @param bytes byte[]
     * @param radix 可以转换进制的范围，从Character.MIN_RADIX到Character.MAX_RADIX，超出范围后变为10进制
     * @return 转换后的字符串
     */
    private static String binary(byte[] bytes, int radix) {
        return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数
    }

    public static String toHex(byte[] bytes) {
        return binary(bytes, 16);
    }

    public static byte[] fromHex(String hex) {
        return new BigInteger(hex, 16).toByteArray();
    }

    /**
     * base 64 encode
     *
     * @param bytes 待编码的byte[]
     * @return 编码后的base 64 code
     */
    public static String toBase64(byte[] bytes) {
        return new BASE64Encoder().encode(bytes);
    }

    /**
     * base 64 decode
     *
     * @param base64Code 待解码的base 64 code
     * @return 解码后的byte[]
     */
    public static byte[] fromBase64(String base64Code) {
        try {
            return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * BASE64URL编码的流程：
     * 1、明文使用BASE64进行加密
     * 2、在BASE64的基础上进行一下的编码：
     * 1)去除尾部的"="
     * 2)把"+"替换成"-"
     * 3)把"/"替换成"_"
     *
     * @param ori
     * @return
     */
    public static String toBase64UrlEncode(byte[] ori) {
        String s = toBase64(ori);
        s = s.split("=")[0];
        s = s.replace('+', '-');
        s = s.replace('/', '_');
        return s;
    }

    /**
     * BASE64URL解码的流程：
     * 1、把BASE64URL的编码做如下解码：
     * 1)把"-"替换成"+"
     * 2)把"_"替换成"/"
     * 3)(计算BASE64URL编码长度)%4
     * a)结果为0，不做处理
     * b)结果为2，字符串添加"=="
     * c)结果为3，字符串添加"="
     * 2、使用BASE64解码密文，得到原始的明文
     *
     * @param encStr
     * @return
     */
    public static byte[] fromBase64UrlEncode(String encStr) {
        String s = encStr;
        s = s.replace('-', '+');
        s = s.replace('_', '/');
        switch (s.length() % 4) { // Pad with trailing '='s
            case 0:
                break; // No pad chars in this case
            case 2:
                s += "==";
                break; // Two pad chars
            case 3:
                s += "=";
                break; // One pad char
            default:
                System.err.println("Illegal base64url String!");
        }
        return fromBase64(s); // Standard base64 decoder
    }


    //--------------------------------------------------
    //加密：
    //1、md5单向加密： md5 、 md5ToBase64
    //2、AES对称加密:
    // 1)、AES加密后用Hex编码：aesEncrypt-aesDecrypt
    // 2)、AES加密后用Base64编码：aesEncryptBase64-aesDecryptBase64
    //todo 3、不对称加密
    //--------------------------------------------------

    /**
     * 获取byte[]的md5值
     *
     * @param bytes byte[]
     * @return md5
     * @throws Exception
     */
    public static byte[] md5(byte[] bytes) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(bytes);
            return md.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static byte[] md5(String msg) {
        return StringUtils.isEmpty(msg) ? null : md5(msg.getBytes());
    }

    public static String md5ToHex(String msg) {
        return toHex(md5(msg));
    }
    /**
     * md5加密后以Base64编码
     *
     * @param msg 待加密字符串
     * @return 获取md5后转为base64
     * @throws Exception
     */
    public static String md5ToBase64(String msg) throws Exception {
        return StringUtils.isEmpty(msg) ? null : toBase64(md5(msg));
    }

    /**
     * AES加密
     *
     * @param content    待加密的内容
     * @param encryptKey 加密密钥
     * @return 加密后的byte[]
     * @throws Exception
     */
    private static byte[] aesEncryptToBytes(String content, String encryptKey) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128, new SecureRandom(encryptKey.getBytes()));
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
            return cipher.doFinal(content.getBytes("utf-8"));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES解密
     *
     * @param encryptBytes 待解密的byte[]
     * @param decryptKey   解密密钥
     * @return 解密后的String
     * @throws Exception
     */
    private static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128, new SecureRandom(decryptKey.getBytes()));
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
            byte[] decryptBytes;
            decryptBytes = cipher.doFinal(encryptBytes);
            return new String(decryptBytes);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * AES加密后，以Hex编码
     *
     * @param content    待加密的内容
     * @param encryptKey 加密密钥
     * @return 加密后的Hex code
     * @throws Exception
     */
    public static String aesEncrypt(String content, String encryptKey) {
        return toHex(aesEncryptToBytes(content, encryptKey));
    }

    public static String aesEncrypt(String content) {
        return aesEncrypt(content, AES_KEY);
    }

    /**
     * 将Hex code AES解密
     *
     * @param encryptStr 待解密的Hex code
     * @param decryptKey 解密密钥
     * @return 解密后的string
     * @throws Exception
     */
    public static String aesDecrypt(String encryptStr, String decryptKey) {
        return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(fromHex(encryptStr), decryptKey);
    }

    public static String aesDecrypt(String encryptStr) {
        return aesDecrypt(encryptStr, AES_KEY);
    }


    /**
     * AES加密后，使用base64编码
     *
     * @param content    待加密的内容
     * @param encryptKey 加密密钥
     * @return 加密后的base 64 code
     * @throws Exception
     */
    public static String aesEncryptBase64(String content, String encryptKey) {
        return toBase64(aesEncryptToBytes(content, encryptKey));
    }

    public static String aesEncryptBase64(String content) {
        return aesEncrypt(content, AES_KEY);
    }

    /**
     * 将base 64 code AES解密
     *
     * @param encryptStr 待解密的base 64 code
     * @param decryptKey 解密密钥
     * @return 解密后的string
     * @throws Exception
     */
    public static String aesDecryptBase64(String encryptStr, String decryptKey) {
        return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(fromBase64(encryptStr), decryptKey);
    }

    public static String aesDecryptBase64(String encryptStr) {
        return aesDecrypt(encryptStr, AES_KEY);
    }
}
